/* rcc.c
 * (c) Tom Trebisky  7-2-2017
 */

/* The reset and clock control module */
struct rcc {
	volatile unsigned long ccr;     /* 时钟控制寄存器 */
	volatile unsigned long cfg;     /* 时钟配置寄存器 */
	volatile unsigned long cir;     /* 时钟中断寄存器 */
	volatile unsigned long apb2;	/* APB2外设复位寄存器 */
	volatile unsigned long apb1;	/* APB1 外设复位寄存器 */
	volatile unsigned long ahbe;	/* AHB 外设时钟使能寄存器 */
	volatile unsigned long ape2;	/* APB2 外设时钟使能寄存器 */
	volatile unsigned long ape1;	/* APB1 外设时钟使能寄存器 */
	volatile unsigned long rev1;	/* 20 MM32不存在此寄存器 */
	volatile unsigned long csr;		/* 24 控制状态寄存器 */
	volatile unsigned long rev2;	/* 0x28 保留 */
	volatile unsigned long rev3;	/* 0x2c 保留 */
	volatile unsigned long rev4;	/* 0x30 保留 */
	volatile unsigned long rev5;	/* 0x34 保留 */
	volatile unsigned long rev6;	/* 0x38 保留 */
	volatile unsigned long rev7;	/* 0x3c 保留 */
	volatile unsigned long syscfg;	/* 0x40 系统配置寄存器 */
};

//MM32L0 RCC 地址 0x4002_1000 - 0x4002_13ff
#define RCC_BASE	(struct rcc *) 0x40021000

//MM32L0 Flash接口地址 0x4002_2000 - 0x4002_23ff
//访问控制寄存器
#define FLASH_ACR	((volatile unsigned long *) 0x40022000)
#define FLASH_PREFETCH	0x0010	/* 预取缓冲区使能 */
#define FLASH_HCYCLE	0x0008	/* 使能flash半周期访问 */

//flash延迟，mm32l0只支持2种
#define FLASH_WAIT0	0x0000	/* 0个等待状态，sysclk <= 24 Mhz */
#define FLASH_WAIT1	0x0001	/* 1个等待状态，24Mhz < sysclk <= 48 Mhz */



/* MM32 GPIO在 AHB上 */
#define GPIOA_ENABLE	0x20000
#define GPIOB_ENABLE	0x40000
#define GPIOC_ENABLE	0x80000
#define GPIOD_ENABLE	0x100000

//MM32 APB2 上 ADC1 SPI1...
#define TIMER1_ENABLE	0x0800
#define UART1_ENABLE	0x4000

//MM32 APB1
#define TIMER2_ENABLE	0x0001
#define TIMER3_ENABLE	0x0002
#define UART2_ENABLE	0x20000
#define USB_ENABLE		0x800000

//MM32 APB1 复位寄存器
#define USB_RESET		0x800000

/* The apb2 and apb1 registers hold reset control bits */

/* 时钟控制寄存器 CCR 位定义 */
#define PLL_ENABLE	0x01000000		//PLL使能位
#define PLL_LOCK	0x02000000		//PLL锁定标志
#define HSI_ON		1				//内部8M时钟开启
#define HSE_ON		0x10000			//外部高速时钟震荡器开启
//#define HSE_TRIM	0x80
#define HSE_RDY		0x20000			//外部高速时钟就绪,只读
//#define CCR_NORM	(HSI_ON | HSE_ON | HSE_TRIM)
#define CCR_NORM	(HSI_ON | HSE_ON | HSE_RDY)

//PLL DM和DN
#define PLL_DM_1 	0
#define PLL_DM_2 	(1<<20)
#define PLL_DM_3 	(2<<20)
#define PLL_DM_4 	(3<<20)
#define PLL_DM_5 	(4<<20)
#define PLL_DM_6 	(5<<20)
#define PLL_DM_7 	(6<<20)
#define PLL_DM_8 	(7<<20)

#define PLL_DN_1	0
#define PLL_DN_2	(1<<26)
//5bit ...
#define PLL_DN_6	(5<<26)
#define PLL_DN_32	(31<<26)
#if 0
#define PLL_2		0
#define PLL_4		(2<<18)
#define PLL_5		(3<<18)
#define PLL_6		(4<<18)
#define PLL_7		(5<<18)
#define PLL_8		(6<<18)
#define PLL_9		(7<<18)	/* multiply by 9 to get 72 Mhz */

/* It works to run at 80, but we may warp the universe ... */
#define PLL_10		(8<<18)	/* XXX - danger !! */
#endif

/* 时钟配置寄存器 CFGR */
//时钟源
#define	SYS_HSI		0x00	// 复位值 内部8 Mhz 6分频作为系统时钟
#define	SYS_HSE		0x01	// 外部时钟作为系统时钟 
#define	SYS_PLL		0x02	// PLL作为系统时钟
#define SYS_LSI		0x03	// LSI作为系统时钟

#define AHB_DIV2	0x80	//AHB = sysclk 2分频

#define APB1_DIV2	(4<<8)	//低速APB1 时钟PCLK1 = HCLK/2
#define APB1_DIV4	(5<<8)	//低速APB1 时钟PCLK1 = HCLK/4

#define APB2_DIV2	(4<<11)	//高速APB2 时钟PCLK2 = HCLK/2
#define APB2_DIV4	(5<<11)	//高速APB2 时钟PCLK2 = HCLK/2

//PLL 时钟源
#define PLL_HSI		0x00000	//HSI 4分频后作为PLL输入时钟
#define PLL_HSE		0x10000	//外部时钟HSE作为PLL输入时钟
#define PLL_XTPRE	0x20000	//??
#define PLL_HSE2	0x30000	//外部时钟HSE2分频作为PLL时钟



//随便定义，后续再改
#define PCLK1		24000000
#define PCLK2		48000000

/* To use USB, we must run the clock at 48 or 72 since we can only
 * divide by 1.0 or 1.5 to get the USB clock, which must be 48
 * Set this bit in the CFG register to divide by 1.0 (else get 1.5).
 * 72/1.5 = 48
 */
#define USB_NODIV	0x400000

int get_pclk1 ( void )
{
	return PCLK1;
}

int get_pclk2 ( void )
{
	return PCLK2;
}

/* The processor comes out of reset using HSI (an internal 8 Mhz RC clock)
 * This sets a 9x multiplier for the PLL to give us 72 Mhz.
 * Note that we do NOT set the USB_NODIV bit, so this gets divided
 * by 1.5 to give us the 48 Mhz USB clock we need.
 */
static void rcc_clocks ( void )
{
	struct rcc *rp = RCC_BASE;
	//HSI 8M 6分频作为系统时钟
	//PLL 源选为HSE
	rp->cfg =  PLL_HSE | SYS_HSI | APB1_DIV2;


	//打开外部晶振
	/* How you set this is tricky
	 * Using |= fails.  Consider the bit band access.
	 * Setting the entire register works.
	 */
	//外部晶振8M，需要6倍频 8*DN/DM=48,DM=1,DN=6
	//HSI 开启
	//HSE 开启
	//PLL 开启
	rp->ccr = PLL_DN_6 | PLL_DM_1 | CCR_NORM | PLL_ENABLE;

	//等待PLL锁定
	while ( ! (rp->ccr & PLL_LOCK ) )
	   ;

	/* Need flash wait states when we boost the clock */
	* FLASH_ACR = FLASH_PREFETCH | FLASH_WAIT1;

	//切换时钟源 HSE
	rp->cfg = PLL_HSE  | SYS_PLL | APB1_DIV2;
}

void rcc_init ( void )
{
	struct rcc *rp = RCC_BASE;

	/* set up the main (PLL) clock */
	rcc_clocks ();

	//MM32 GPIO AHB 上
	rp->ahbe |= GPIOA_ENABLE;
	rp->ahbe |= GPIOB_ENABLE;

	/* Turn on USART 1 */
	rp->ape2 |= UART1_ENABLE;

	rp->ape1 |= TIMER2_ENABLE;

	rp->ape1 |= USB_ENABLE;

	// rp->ape1 |= UART2_ENABLE;
	// rp->ape1 |= UART3_ENABLE;

}

void
rcc_usb_reset ( void )
{
	struct rcc *rp = RCC_BASE;

	rp->apb1 &= ~USB_RESET;
}

/* THE END */
